#pragma once
#include <iostream>
#include <string>
#include <unordered_map>
#include <sys/epoll.h>
#include <functional>
#include <cassert>

#include "Sock.hpp"
#include "Log.hpp"
#include "Epoller.hpp"
#include "Util.hpp"
#include "Protocal.hpp"

using namespace protocol_ns;

const static uint16_t default_port = 8080;
const static int bufsize = 1024;

class Connection;
using callback_t = std::function<void(Connection *)>;

using func_t = std::function<Response(const Request &)>;

class Connection
{
public:
    Connection(const int &fd, const std::string &ip, const uint16_t &port)
        : fd_(fd), ip_(ip), port_(port)
    {
    }

    void Register(callback_t recever, callback_t sender, callback_t excepter)
    {
        recever_ = recever;
        sender_ = sender;
        excepter_ = excepter;
    }

public:
    // IO信息
    int fd_;
    std::string inbuffer_;
    std::string outbuffer_;

    // User信息
    std::string ip_;
    uint16_t port_;

    // IO处理函数
    callback_t recever_;
    callback_t sender_;
    callback_t excepter_;

    // 自己要关心的事件
    uint32_t events_;
};

class EpollServer
{
    const static int gnum = 64;

public:
    EpollServer(func_t func, const uint16_t port = default_port)
        : port_(port), func_(func)
    {
    }

    void initServer()
    {
        listensock_.Socket();
        listensock_.Bind(port_);
        listensock_.Listen();

        epoller_.Create(); // 创建epoll模型
        AddConnection(listensock_.Fd(), EPOLLIN | EPOLLET);
    }

    void DisPatcher()
    {
        int timeout = 1000;
        while (true)
        {
            LoopOnce(timeout);
        }
    }

    void LoopOnce(int timeout)
    {
        int n = epoller_.Wait(revs_, gnum, timeout);

        // revs_里的是就绪的事件
        for (int i = 0; i < n; i++)
        {
            int fd = revs_[i].data.fd;
            uint32_t events = revs_[i].events;
            logMessage(Debug, "当前正在处理%d", fd);

            if ((events & EPOLLERR) || (events & EPOLLHUP))
                events |= (EPOLLIN | EPOLLOUT);

            if (events & EPOLLIN && ConnIsExist(fd))
                connections_[fd]->recever_(connections_[fd]);
            if (events & EPOLLOUT && ConnIsExist(fd))
                connections_[fd]->sender_(connections_[fd]);
        }
    }

    void AddConnection(int fd, uint32_t events,
                       const std::string &ip = "127.0.0.1", uint16_t port = default_port)
    {
        // 1设置非阻塞(ET)
        if (events & EPOLLET)
            Util::setNonBlock(fd);

        // 2写入connections_
        Connection *conn = new Connection(fd, ip, port);
        if (fd == listensock_.Fd())
        {
            conn->Register(std::bind(&EpollServer::Accepter, this, std::placeholders::_1), nullptr, nullptr);
        }
        else
        {
            conn->Register(std::bind(&EpollServer::Recever, this, std::placeholders::_1),
                           std::bind(&EpollServer::Sender, this, std::placeholders::_1),
                           std::bind(&EpollServer::Excepter, this, std::placeholders::_1));
        }

        conn->events_ = events;

        connections_.insert(std::pair<int, Connection *>(fd, conn));

        // 3 写入epoll内核
        bool res = epoller_.AddModEvent(fd, events, EPOLL_CTL_ADD);
        assert(res);
        (void)res;

        // log...
        logMessage(Debug, "AddConnection success, fd: %d, clientinfo: [%s:%d]", fd, ip.c_str(), port);
    }

    // 连接管理器
    void Accepter(Connection *conn)
    {
        do
        {
            int err = 0;

            // sock
            std::string clientip;
            uint16_t clientport;
            int sock = listensock_.Accept(&clientip, &clientport, &err);

            if (sock > 0)
            {
                logMessage(Debug, "%s:%d 已经连上了服务器了", clientip.c_str(), clientport);
                AddConnection(sock, EPOLLIN | EPOLLET, clientip, clientport);
            }
            else
            {
                // 底层数据读完了 break
                if (err == EAGAIN || err == EWOULDBLOCK)
                    break;
                else if (err == EINTR)
                    continue;
                else
                {
                    logMessage(Warning, "errstring : %s, errcode: %d", strerror(err), err);
                    continue;
                }
            }

        } while (conn->events_ & EPOLLET);

        logMessage(Debug, "Accept done");
    }

    bool ReceverHelper(Connection *conn)
    {
        bool ret = true;
        do
        {
            char buffer[bufsize];
            ssize_t n = recv(conn->fd_, buffer, sizeof(buffer) - 1, 0);
            if (n > 0)
            {
                buffer[n] = 0;
                conn->inbuffer_ += buffer;
            }
            else if (n == 0)
            {
                // 说明对方关闭了连接
                conn->excepter_(conn);
                ret = false;
                break;
            }
            else
            {
                if (errno == EAGAIN || errno == EWOULDBLOCK)
                    break;
                else if (errno == EINTR)
                    continue;
                else
                {
                    conn->excepter_(conn);
                    ret = false;
                    break;
                }
            }

        } while (conn->events_ & EPOLLET);

        return ret;
    }

    void HandlerRequest(Connection *conn)
    {
        int quit = false;
        while (!quit)
        {
            std::string requestStr;
            // 1 提取1个完整报文
            int n = protocol_ns::ParsePackage(conn->inbuffer_, &requestStr);
            if (n > 0)
            {
                // 2 提取有效载荷
                requestStr = RemoveHeader(requestStr, n);

                // 3 反序列化
                Request req;
                req.Deserialize(requestStr);

                // 4 业务处理
                Response resp = func_(req);

                // 5 序列化
                std::string respStr;
                resp.Serialize(&respStr);

                // 6 添加报头
                respStr = AddHeader(respStr);

                // 7 返回
                conn->outbuffer_ += respStr;
            }
            else
                quit = true;
        }
    }

    void Recever(Connection *conn)
    {
        // 这里要避免出现异常删除conn时，影响后面的代码
        if (!ReceverHelper(conn))
            return;

        // 处理响应
        HandlerRequest(conn);

        // 写入时，先直接写入，没有写完再交给epoll去写
        if (!conn->outbuffer_.empty())
        {
            // 触发第一次发送
            conn->sender_(conn);
        }
    }

    void Sender(Connection *conn)
    {
        bool safe = true;
        do
        {
            ssize_t n = send(conn->fd_, conn->outbuffer_.c_str(), conn->outbuffer_.size(), 0);
            if (n > 0)
            {
                conn->outbuffer_.erase(0, n);

                // 发完退出
                if (conn->outbuffer_.empty())
                    break;
            }
            else
            {
                if (errno == EAGAIN || errno == EWOULDBLOCK)
                    break;
                else if (errno == EINTR)
                    continue;
                else
                {
                    safe = false;
                    conn->excepter_(conn);
                    break;
                }
            }
        } while (conn->events_ & EPOLLET);

        // 发生异常就返回
        if (!safe)
            return;

        // 不为空再让epoll代理
        if (!conn->outbuffer_.empty())
            EnableReadWrite(conn, true, true);
        else
            EnableReadWrite(conn, true, false);
    }

    void Excepter(Connection *conn)
    {
        // 1 先从epoll移除fd
        epoller_.DelEvent(conn->fd_);

        // 2 移除 kv关系
        connections_.erase(conn->fd_);

        // 3 关闭fd
        close(conn->fd_);

        // 4 释放conn对象
        
        //only debug
        int fd = conn->fd_;
        std::string ip = conn->ip_;
        uint16_t port = conn->port_;
        delete conn;

        logMessage(Debug, "Excepter...done, fd: %d, clientinfo: [%s:%d]",fd,ip.c_str(),port);
    }

    void EnableReadWrite(Connection *conn, bool read, bool write)
    {
        conn->events_ = (read ? EPOLLIN : 0) | (write ? EPOLLOUT : 0) | EPOLLET;
        epoller_.AddModEvent(conn->fd_, conn->events_, EPOLL_CTL_MOD);
    }

    bool ConnIsExist(int fd)
    {
        return connections_.find(fd) != connections_.end();
    }

private:
    func_t func_;
    uint16_t port_;
    Sock listensock_;
    Epoller epoller_;
    struct epoll_event revs_[gnum];
    std::unordered_map<int, Connection *> connections_;
};
